Skip to content

Spring IOC 容器初始化与循环依赖

一句话机制

Spring 通过 AbstractApplicationContext.refresh()一个方法完成容器的全部启动工作;Bean 的创建与循环依赖的化解,都发生在这套流程里的 finishBeanFactoryInitialization 阶段,靠三级缓存提前暴露"半成品 Bean"的引用(必要时是 AOP 代理)来打破闭环。

容器初始化主体流程(refresh 12 步)

  1. prepareRefresh() — 刷新前预处理(启动时间、环境校验)
  2. obtainFreshBeanFactory() — 拿到 DefaultListableBeanFactory,加载并注册 BeanDefinitionBeanDefinitionRegistry(本质是一个 Map<beanId, BeanDefinition>
  3. prepareBeanFactory() — 给 BeanFactory 做基础设置(类加载器等)
  4. postProcessBeanFactory() — 留给子类的后置钩子
  5. invokeBeanFactoryPostProcessors() — 实例化并调用 BeanFactoryPostProcessor
  6. registerBeanPostProcessors() — 注册 BeanPostProcessor(创建 Bean 前后执行)
  7. initMessageSource() — 国际化
  8. initApplicationEventMulticaster() — 事件派发器
  9. onRefresh() — 子类自定义逻辑(如内嵌 Web 服务器)
  10. registerListeners() — 注册 ApplicationListener
  11. finishBeanFactoryInitialization()实例化所有非懒加载单例 Bean(含属性填充、初始化、后置处理)
  12. finishRefresh() — 发布 ContextRefreshedEvent

关键心智模型:容器 ≠ 一个 Map。Map(单例池 singletonObjects)只是容器的一个成员;容器是一组组件(BeanFactory、单例池、BeanPostProcessor…)加它们之间的协作流程。

Bean 生命周期(finishBeanFactoryInitialization 内部 7 步)

  1. 实例化(构造函数,未设属性)
  2. 属性填充(populateBean)
  3. 若实现 Aware 接口,调用对应方法
  4. BeanPostProcessor.postProcessBeforeInitialization()
  5. InitializingBean.afterPropertiesSet()
  6. 自定义 init-method
  7. BeanPostProcessor.postProcessAfterInitialization()

循环依赖

  • 能解决哪种set/字段注入(setter)方式的循环依赖。
  • 解决不了哪种:构造器注入的循环依赖(实例化前就需要对方,来不及提前暴露)。
  • 解法:三级缓存(见下)。

三级缓存数据结构

级别字段内容
一级singletonObjects完整版 Bean(初始化完成)
二级earlySingletonObjects已实例化但未属性赋值的 Bean(或代理)
三级singletonFactories能生成该 Bean 的 ObjectFactory(可产出代理)

为什么是三级而不是两级?

两级缓存足以解决"原始对象"的循环依赖,但无法处理"循环依赖 + AOP"同时出现:若 A↔B 且 A 配了 AOP,我们真正要拿到的是 A 的代理对象。只有三级缓存用 ObjectFactory(即 getEarlyBeanReference)在需要时才决定产出原始对象还是代理,从而保证 B 注入的 A 引用与最终容器里的 A 是同一个代理对象

关键源码路径(A↔B 场景)

  1. 创建 A:doCreateBeancreateBeanInstance(仅构造)→ addSingletonFactory 把 A 的工厂放进三级缓存
  2. populateBean(A) 发现要注入 B → getBean(B)
  3. 创建 B 同理,B 也进三级缓存;populateBean(B) 要注入 A → getSingleton("A", allowEarlyReference=true)
  4. 一级、二级都查不到 → 从三级缓存的工厂取 getEarlyBeanReference("A") → 产出 A 的(代理)对象放入二级缓存、从三级缓存移除
  5. B 属性填充完成 → getSingleton("B") 把完整 B 放入一级缓存、清理二/三级
  6. 回到 A 的 populateBean:此时从二级缓存拿到代理后的 A,注入给 B 的引用 ✓;A 继续完成初始化,最终 addSingleton 把完整 A(就是那个代理)放入一级缓存

不变量:最终从容器中拿到的 A、B 互相引用的都是同一份对象(A 为代理时,B 持有的是同一代理)

常见误解

  • "循环依赖靠两级缓存就够了" —— 错,遇到 AOP 会拿到不一致的对象。
  • "容器就是个 Map" —— 错,Map 只是单例池。
  • "构造器循环依赖 Spring 能解" —— 错,构造器循环依赖直接抛 BeanCurrentlyInCreationException

关联

  • 上游:Spring容器初始化主体流程 · Spring是如何解决循环依赖
  • 同域:[Spring AOP与声明式事务](./Spring AOP与声明式事务)(AOP 代理正是三级缓存里 getEarlyBeanReference 的产物)
  • 域地图:Java后端
最近更新